<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>Web Components 完整示例</title>
  <style>
    /* 页面全局样式，用于对比 Web Component 的样式封装 */
    body {
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
      line-height: 1.6;
      padding: 20px;
      background-color: #f4f4f4;
    }
    h1, h2 {
      border-bottom: 2px solid #ccc;
      padding-bottom: 10px;
    }
  </style>
</head>
<body>

  <h1>Web Components 综合学习示例</h1>

  <!-- ================================================================= -->
  <!-- 示例一 & 二：简单的自定义元素，并使用 Shadow DOM 封装样式 -->
  <!-- ================================================================= -->
  <h2>示例 1 & 2: 简单的自定义元素与 Shadow DOM</h2>
  <p>这是一个最基础的自定义元素，它的样式（红色、粗体、蓝色边框）被完全封装在 Shadow DOM 中，不会影响页面其他部分。</p>
  <hello-world></hello-world>


  <!-- ================================================================= -->
  <!-- 示例三 & 四：使用 Template, Slot, Attributes 和生命周期回调 -->
  <!-- ================================================================= -->
  <h2>示例 3 & 4: 复杂组件 (Template, Slot, Attribute, Lifecycle)</h2>
  <p>这是一个更复杂的用户卡片组件。它使用 <code>&lt;template&gt;</code> 来提高效率，使用 <code>&lt;slot&gt;</code> 来接收外部内容，并通过 <code>avatar</code> 属性来动态设置图片。</p>

  <!-- 组件的模板，它在页面上不可见 -->
  <template id="user-card-template">
    <style>
      /* 这些样式被 Shadow DOM 封装，只在组件内部生效 */
      .card {
        font-family: sans-serif;
        background: #ffffff;
        border: 1px solid #ccc;
        border-radius: 8px;
        padding: 15px;
        width: 280px;
        display: flex;
        align-items: center;
        box-shadow: 0 2px 5px rgba(0,0,0,0.1);
        margin-bottom: 15px;
      }
      .avatar {
        width: 60px;
        height: 60px;
        border-radius: 50%;
        margin-right: 15px;
        background-color: #eee; /* 默认背景色 */
      }
      .details {
        flex: 1;
      }
      .details h3 {
        margin: 0 0 5px;
        font-size: 1.2em;
        color: #333;
      }
      .details p {
        margin: 0;
        font-size: 0.9em;
        color: #777;
      }
    </style>
    <div class="card">
      <img class="avatar" src="" alt="User Avatar">
      <div class="details">
        <h3>
          <!-- "username" 插槽，如果没有提供内容，则显示默认值 "默认用户" -->
          <slot name="username">默认用户</slot>
        </h3>
        <p>
          <!-- "email" 插槽 -->
          <slot name="email">no-email@example.com</slot>
        </p>
      </div>
    </div>
  </template>

  <!-- 第一个 user-card 实例，提供了所有数据 -->
  <user-card avatar="https://i.pravatar.cc/150?u=zhangsan">
    <span slot="username">张三</span>
    <span slot="email">zhangsan@example.com</span>
  </user-card>

  <!-- 第二个 user-card 实例，没有提供 email，将显示 slot 的默认内容 -->
  <user-card avatar="https://i.pravatar.cc/150?u=lisi">
    <span slot="username">李四</span>
  </user-card>

  <!-- 第三个 user-card 实例，所有内容都使用默认值 -->
  <user-card></user-card>


  <!-- ================================================================= -->
  <!-- 所有 Web Components 的 JavaScript 定义 -->
  <!-- ================================================================= -->
  <script>
    // ----------------------------------------------------
    // 定义 <hello-world> 组件
    // ----------------------------------------------------
    class HelloWorld extends HTMLElement {
      constructor() {
        super(); // 必须首先调用 super()

        // 1. 创建一个 Shadow Root (影子根)
        const shadow = this.attachShadow({ mode: 'open' });

        // 2. 创建组件内部的元素
        const wrapper = document.createElement('span');
        wrapper.textContent = 'Hello, World!';

        const style = document.createElement('style');
        style.textContent = `
          span {
            color: red;
            font-weight: bold;
            padding: 10px;
            border: 1px solid blue;
            border-radius: 5px;
          }
        `;

        // 3. 把创建的元素附加到 Shadow DOM 上
        shadow.appendChild(style);
        shadow.appendChild(wrapper);
      }
    }
    // 注册新元素
    customElements.define('hello-world', HelloWorld);


    // ----------------------------------------------------
    // 定义 <user-card> 组件
    // ----------------------------------------------------
    class UserCard extends HTMLElement {
      constructor() {
        super();
        
        // 获取页面中的模板
        const template = document.getElementById('user-card-template');
        const templateContent = template.content;

        // 为组件实例创建 Shadow DOM
        const shadow = this.attachShadow({ mode: 'open' });
        
        // 将模板内容的深拷贝附加到 Shadow DOM
        shadow.appendChild(templateContent.cloneNode(true));
      }
      
      // 1. 指定要观察哪些属性的变化
      static get observedAttributes() {
        return ['avatar'];
      }
      
      // 2. 当元素被插入到 DOM 中时调用
      connectedCallback() {
        console.log(`'${this.querySelector('[slot=username]')?.textContent || '默认用户'}' 的 user-card 已被添加到页面！`);
        // 组件加载时，检查并更新一次头像
        this.updateAvatar(this.getAttribute('avatar'));
      }

      // 3. 当被观察的属性变化时调用
      attributeChangedCallback(name, oldValue, newValue) {
        console.log(`属性 ${name} 从 ${oldValue} 变为 ${newValue}`);
        if (name === 'avatar') {
          this.updateAvatar(newValue);
        }
      }
      
      // 4. 当元素从 DOM 中移除时调用
      disconnectedCallback() {
        console.log(`'${this.querySelector('[slot=username]')?.textContent || '默认用户'}' 的 user-card 已从页面移除！`);
      }

      // 辅助方法，用于更新头像
      updateAvatar(url) {
        // this.shadowRoot 用来访问组件的 Shadow DOM
        const img = this.shadowRoot.querySelector('.avatar');
        if (url) {
          img.src = url;
        } else {
          // 如果没有提供 avatar 属性，可以设置一个默认图片或留空
          img.src = " MjA1IDAgMy43MSAxLjY2IDMuNzEgMy43MSAwIDIuMDUzLTEuNjYgMy43MS0zLjcxIDMuNzEtMi4wNTIgMC0zLjcxLTEuNjU3LTMuNzEtMy43MSAwLTIuMDQ4IDEuNjU4LTMuNzEgMy43MS0zLjcxeiBtMCAxNi44Yy0yLjUgMC00LjcxLTEuMjgtNi4yNS0zLjE1LjAzLTEuOTggMy45OS0zLjEgNi4yNS0zLjFzNi4yMiAxLjEyIDYuMjUgMy4xNUMxNi43MSA MTcuNTI4IDE0LjUgMTguOCAxMiAxOC44eiIvPjwvc3ZnPg==";
        }
      }
    }
    // 注册新元素
    customElements.define('user-card', UserCard);
  </script>

</body>
</html>
